﻿//-----------------------------------------------------------------------------
// File: ConfigManager.cs
// Author: Pierre Biermann (tauron031@gmail.com)
// Project: Bibliotek
// Namespace(s): MyLib.System.IO
//
// Desc: Klassen zur Verwaltung der Programmkonfiguration
//       
// (c) 2009-2013 by Pierre Biermann
//-----------------------------------------------------------------------------

using System;
using System.Windows;
using System.Xml.Serialization;
using System.IO;
using System.Reflection;
using System.Diagnostics;

namespace MyLib.System.IO
{
    /// <summary>
    /// Induviduelle oder allgemeine Benutzerdaten
    /// </summary>
    public enum ConfigDomain
    {
        /// <summary>
        /// induviduelle Benutzerdaten
        /// </summary>
        UserDomain,
        /// <summary>
        /// allgemeine Benutzerdaten
        /// </summary>
        ApplicationDomain
    }

    /// <summary>
    /// Diese Schnittstelle kann von der Konfigurationsklasse implementiert werden, 
    /// wenn eine Benachrichtigung vor dem speichern und nach dem laden erfolgen soll.
    /// </summary>
    public interface IConfigNotifier
    {
        /// <summary>
        /// Wird vor dem speichern aufgerufen.
        /// </summary>
        void BeforeSave();
        /// <summary>
        /// Wird nach dem laden aufgerufen.
        /// </summary>
        void AfterLoad();
    }

    /// <summary>
    /// Klasse die einen globalen Konfigurationsmanager für alle Programmeinstellungen
    /// die nach beenden der Anwendung erhalten bleiben sollen bereitstellt.
    /// </summary>
    /// <typeparam name="genericConfig"></typeparam>
    public class ConfigManager<genericConfig> where genericConfig : class, new()
    {
        string _configName;
        string _configFilePath;
        genericConfig _currentConfig;

        /// <summary>
        /// Erstellt eine neue ConfigManager-Objektinstanz.
        /// </summary>
        /// <param name="mainWindow">Hauptanwendungsfenster (WPF)</param>
        /// <param name="domain">Induviduelle oder allgemeine Benutzerdaten</param>
        public ConfigManager(Window mainWindow, ConfigDomain domain)
        {
            // Felder initialisieren
            _currentConfig = new genericConfig();
            Assembly assembly = Assembly.GetExecutingAssembly();
            string applicationData;
            if (domain == ConfigDomain.UserDomain)
                applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); // Pfad zu Benutzer-Anwendungsdaten.
            else
                applicationData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); // Pfad zu Benutzerübergreifenden Anwendungsdaten.
            string productVersion = assembly.GetName().Version.ToString();
            string productName = assembly.GetName().Name;
            _configFilePath = applicationData + '\\' + productName + '\\' + productVersion;
            _configName = productName + ".config";

            // Ereignis-Handler des Hauptanwendungsfenster registrieren.
            if (mainWindow != null)
            {
                mainWindow.Closed += new EventHandler(mainWindow_Closed);
            }

            // Konfiguration laden
            LoadConfig();
        }

        /// <summary>
        /// Liefert den vollständigen Konfigurationspfad.
        /// </summary>
        /// <returns></returns>
        public string GetFullConfigFilePath()
        {
            return _configFilePath + '\\' + _configName;
        }

        /// <summary>
        /// Liefert oder setzt den Konfiguartions-Dateinamen.
        /// </summary>
        public string ConfigName
        {
            get { return _configName; }
            set { _configName = value; }
        }

        /// <summary>
        /// Liefert oder setzt den Dateipfad zum Konfigurationsverzeichnis.
        /// </summary>
        public string ConfigFilePath
        {
            get { return _configFilePath; }
            set { _configFilePath = value; }
        }

        /// <summary>
        /// Erweitert die angegebene Datei um den Konfigurationspfad.
        /// </summary>
        /// <param name="sourceFile">Quelldateiname</param>
        /// <returns>Vollständiger Konfigurationspfad</returns>
        public string ConfigFile(string sourceFile)
        {
            return _configFilePath + '\\' + sourceFile;
        }

        /// <summary>
        /// Speichert die aktuelle Konfiguration ab.
        /// </summary>
        public virtual void StoreConfig()
        {
            try
            {
                if (_currentConfig is IConfigNotifier)
                {
                    ((IConfigNotifier)_currentConfig).BeforeSave();
                }
                if (!Directory.Exists(ConfigFilePath))
                {
                    Directory.CreateDirectory(ConfigFilePath);
                }
                XmlSerializer serializer = new XmlSerializer(_currentConfig.GetType());
                TextWriter writer = new StreamWriter(GetFullConfigFilePath());
                serializer.Serialize(writer, _currentConfig);
                writer.Close();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                Debug.WriteLine(ex.StackTrace);
            }
        }

        /// <summary>
        /// Lädt eine vorhandene Konfiguration oder setzt die Konfiguration auf Standardwerte, 
        /// falls noch keine Datei existiert.
        /// </summary>
        public virtual void LoadConfig()
        {
            if (File.Exists(GetFullConfigFilePath()))
            {
                try
                {
                    XmlSerializer serializer = new XmlSerializer(_currentConfig.GetType());
                    TextReader reader = new StreamReader(GetFullConfigFilePath());
                    _currentConfig = (genericConfig)serializer.Deserialize(reader);
                    if (_currentConfig is IConfigNotifier)
                    {
                        ((IConfigNotifier)_currentConfig).AfterLoad();
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                    Debug.WriteLine(ex.StackTrace);
                    try
                    {
                        if (HasConfig)
                            File.Delete(GetFullConfigFilePath()); // delete file if damaged or currupted
                    }
                    catch (Exception ex1)
                    {
                        Debug.WriteLine(ex1.Message);
                    }
                }
            }
            else
            {
                _currentConfig = new genericConfig();
            }
        }

        /// <summary>
        /// Liefert oder setzt das aktuelle Konfigurationsobjekt.
        /// </summary>
        public genericConfig Current
        {
            get { return _currentConfig; }
            set { _currentConfig = value; }
        }

        /// <summary>
        /// Prüft ob schon eine Konfigurationsdatei vorhanden ist.
        /// </summary>
        public bool HasConfig
        {
            get { return File.Exists(GetFullConfigFilePath()); }
        }

        private void mainWindow_Closed(object sender, EventArgs e)
        {
            StoreConfig();
        }
    }
}
